/* 
 *	Copyright (C) 2009 cooleyes
 *	eyes.cooleyes@gmail.com 
 *
 *  This Program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *   
 *  This Program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *   
 *  You should have received a copy of the GNU General Public License
 *  along with GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
 *  http://www.gnu.org/copyleft/gpl.html
 *
 */
 
#include "wma_parser.h"
#include "common/mem64.h"
#include <string.h>
#include <pspasfparser.h>

static int asf_read_cb(void *userdata, void *buf, SceSize size) {
	SceUID handle = *((SceUID*)userdata);
	int ret = sceIoRead(handle, buf, size);
	return ret;
}

static SceOff asf_seek_cb(void *userdata, void *buf, SceOff offset, int whence) {
	
	SceUID handle = *((SceUID*)userdata);
	SceOff ret = -1;

	ret = sceIoLseek(handle, offset, whence);

	return ret;
}

WmaParser::WmaParser() {
	initialize();
};

WmaParser::~WmaParser() {
	close();
};

void WmaParser::initialize() {
	is_open = false;
	
	handle = -1;
	
	asfparser = 0;
	
	output_buffer = 0;
	read_buffer = 0;
	
	codec = 0;
	codec_initialize = false;
	
	memset(&init_data, 0, sizeof(AudioCodecInitData));
};

void WmaParser::finalize() {
	if ( codec ) {
		if ( codec_initialize )
			codec->finalize();
		delete codec;
	}
	if ( read_buffer ) {
		free(read_buffer);
	}
	if ( output_buffer ) {
		free(output_buffer);
	}
	if ( !(handle < 0) ) {
		sceIoClose(handle);
	}
	if ( asfparser ) {
		free(asfparser);
	}
	if ( init_data.extra_data ) {
		free(init_data.extra_data);
	}	
	initialize();
};


char* WmaParser::open(const char* filename) {
	
	close();
	
	handle = sceIoOpen(filename, PSP_O_RDONLY, 0777);

	if (handle < 0) {
		finalize();
		return "WmaParser : sceIoOpen fail";
	}
	
	asfparser = malloc_64(sizeof(SceAsfParser));
	
	if ( !asfparser ) {
		finalize();
		return "WmaParser : malloc SceAsfParser fail";
	}
	memset(asfparser, 0, sizeof(SceAsfParser));
	
	SceAsfParser* parser = (SceAsfParser*)asfparser;
	
	parser->iUnk0 = 0x01;
	parser->iUnk1 = 0x00;
	parser->iUnk2 = 0x000CC003;
	parser->iUnk3 = 0x00;
	parser->iUnk4 = 0x00000000;
	parser->iUnk5 = 0x00000000;
	parser->iUnk6 = 0x00000000;
	parser->iUnk7 = 0x00000000;
	
	if ( sceAsfCheckNeedMem(parser) < 0 ) {
		finalize();
		return "WmaParser : sceAsfCheckNeedMem fail";
	}
	
	if ( parser->iNeedMem > 0x8000 ) {
		finalize();
		return "WmaParser : iNeedMem > 0x8000";
	}
	
	init_data.extra_data = (uint8_t*)malloc_64(parser->iNeedMem);
	if ( !init_data.extra_data ) {
		finalize();
		return "WmaParser : malloc extra_data fail";
	}
	init_data.extra_data_len = parser->iNeedMem;
	
	parser->pNeedMemBuffer = init_data.extra_data;
	parser->iUnk3356 = 0x00000000;
	
	if ( sceAsfInitParser(parser, &handle, &asf_read_cb, &asf_seek_cb) < 0 ) {
		finalize();
		return "WmaParser : sceAsfInitParser fail";
	}
	
	init_data.samplerate = *((uint32_t*)(init_data.extra_data+4));
	init_data.channels = *((uint16_t*)init_data.extra_data);	
	init_data.frame_align = *((uint16_t*)(init_data.extra_data+12));	
	init_data.sample_bits = 0x10;
	init_data.samples_per_frame = 2048;
	init_data.samples_per_decoded = init_data.samples_per_frame;
		
	
	output_buffer = malloc_64(init_data.samples_per_frame<<3);
	if ( output_buffer == 0 ) {
		finalize();
		return "WmaParser : malloc output_buffer fail";
	}
	
	read_buffer = malloc_64(init_data.frame_align);
	if ( read_buffer == 0 ) {
		finalize();
		return "WmaParser : malloc read_buffer fail";
	}
	
	codec = AudioCodec::create_audio_codec(AudioCodec::WMA);
	if ( !codec ) {
		finalize();
		return "WmaParser : create audio codec fail";
	}
	char* result = codec->initialize(&init_data);
	if ( result ) {
		finalize();
		return result;
	}
	codec_initialize = true;
	
	is_open = true;
	return 0;
};

void WmaParser::close() {
	if ( is_open ) {
		finalize();
	}
};
	
uint32_t WmaParser::get_samplerate() {
	return init_data.samplerate;
};

uint32_t WmaParser::get_channels() {
	return init_data.channels;
};

uint64_t WmaParser::get_duration() {
	uint64_t duration = ((SceAsfParser*)asfparser)->lDuration/10;
	return duration;
};
	
uint32_t WmaParser::get_samples_per_frame() {
	return init_data.samples_per_frame;
};

uint32_t WmaParser::get_max_samples_per_frame() {
	return (init_data.samples_per_frame<<1);
};

uint64_t WmaParser::seek_time(uint64_t time) {
	uint64_t current = (time > ((SceAsfParser*)asfparser)->lDuration/10) ? ((SceAsfParser*)asfparser)->lDuration/10 : time;
	
	int ms = current/1000;
	sceAsfSeekTime((SceAsfParser*)asfparser, 1, &ms);
	
	return current;
};

char* WmaParser::get_frame(void** pcm, uint32_t* samples) {
	
	if ( !is_open )
		return "WmaParser : parser is not open";
	
	SceAsfParser* parser = (SceAsfParser*)asfparser;
	while(1) {
		memset(read_buffer, 0, init_data.frame_align);
		parser->sFrame.pData = read_buffer;
		if ( sceAsfGetFrameData(parser, 1, &parser->sFrame) < 0 ) {
			return "WmaParser : get frame data fail";
		}
		memset(output_buffer, 0, init_data.samples_per_frame<<3);
		int get_samples = codec->decode(&parser->sFrame, init_data.frame_align, output_buffer);
		if ( get_samples < 0 ) {
			return "WmaParser : decode frame data fail";
		}
		if ( get_samples > 0 ) {
			*pcm = output_buffer;
			*samples = get_samples;
			return 0;
		}
	}
	return 0;
};



